package org.finesys.common.dataPermission.aop;


import lombok.AllArgsConstructor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.finesys.common.dataPermission.annotation.DataPermission;
import org.finesys.common.dataPermission.local.DataPermissionContextHolder;
import org.springframework.core.MethodClassKey;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 自定义Advice 处理数据权限的通知
 * <p/>
 * 1. 在执行方法前 将数据权限注解入栈 2. 在执行方法后 将数据权限注解出栈
 */
@DataPermission
@AllArgsConstructor(staticName = "of")
public class DataPermissionCustomAdvice implements MethodInterceptor {
    /**
     * 方法无数据权限注解时 使用此进行占位
     */
    private static final DataPermission DATA_PERMISSION_NULL = DataPermissionCustomAdvice.class.getAnnotation(DataPermission.class);

    private final Map<MethodClassKey, DataPermission> dataPermissionMap = new ConcurrentHashMap<>();


    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        // 方法执行前 获取方法上的数据权限注解
        DataPermission dataPermission = this.findAnnotation(invocation);
        // 数据权限注解入栈
        if (Objects.nonNull(dataPermission)) {
            DataPermissionContextHolder.add(dataPermission);
        }

        try {
            // 执行目标方法
            return invocation.proceed();
        } finally {
            // 方法执行后 数据权限注解出栈
            if (Objects.nonNull(dataPermission)) {
                DataPermissionContextHolder.remove(dataPermission);
            }
        }
    }

    private DataPermission findAnnotation(MethodInvocation invocation) {
        Method method = invocation.getMethod();
        Object targetObject = invocation.getThis();
        Class<?> classz = Objects.nonNull(targetObject) ? targetObject.getClass() : method.getDeclaringClass();
        MethodClassKey methodClassKey = new MethodClassKey(method, classz);
        //从缓存中获取数据权限注解
        DataPermission dataPermission = dataPermissionMap.get(methodClassKey);
        if (Objects.nonNull(dataPermission)) {
            return dataPermission != DATA_PERMISSION_NULL ? dataPermission : null;
        }
        //优先从方法中获取注解
        dataPermission = AnnotationUtils.findAnnotation(method, DataPermission.class);
        if (Objects.isNull(dataPermission)) {
            dataPermission = AnnotationUtils.findAnnotation(classz, DataPermission.class);
        }
        //方法或者类上有注解则添加到缓存中 没有则添加默认的到缓存
        dataPermissionMap.put(methodClassKey, Objects.nonNull(dataPermission) ? dataPermission : DATA_PERMISSION_NULL);
        return dataPermission;
    }
}
